package com.dht.xiao.jianye.command.base.serialport.syncservices;

import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;

import com.dht.xiao.jianye.command.base.callback.DataPackCallback;
import com.dht.xiao.jianye.command.base.exception.ApiExceptionCode;
import com.dht.xiao.jianye.command.base.exception.BaseSerialPortException;
import com.dht.xiao.jianye.command.base.serialport.CmdPack;
import com.dht.xiao.jianye.command.base.serialport.DataPack;
import com.dht.xiao.jianye.command.base.serialport.callback.BaseDataCallback;
import com.dht.xiao.jianye.command.base.serialport.callback.SendResultCallback;
import com.dht.xiao.jianye.command.base.serialport.log.SerialPortLoggerFactory;
import com.dht.xiao.jianye.command.base.serialport.syncservices.blockingqueue.BaseQueue;
import com.dht.xiao.jianye.command.base.serialport.syncservices.blockingqueue.Priority;
import com.dht.xiao.jianye.command.base.serialport.utils.ByteUtil;
import com.dht.xiao.jianye.dlclogfile.LogFileUtil;
import com.licheedev.myutils.LogPlus;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;


/**
 * @author :      fangbingran
 * @aescription : todo(这里用一句话描述这个类的作用)
 * @date :        2019/06/06  12:22
 */
public class CmdTask extends BaseQueue {
    private SendResultCallback mSendResultCallback;
    private CmdPack mCmdPack;
    private BaseDataCallback mBaseDataCallback;
    private BufferedInputStream mBufferedInputStream;
    private OutputStream mOutputStream;
    private Handler handler  ;
    private Boolean isLog  ;

    public CmdTask(@Priority int priority, Boolean isLog,SendResultCallback mSendResultCallback, CmdPack mCmdPack, OutputStream outputStream,
                   BufferedInputStream bufferedInputStream, BaseDataCallback baseDataCallback) {
        this.mSendResultCallback = mSendResultCallback;
        this.mCmdPack = mCmdPack;
        mBufferedInputStream = bufferedInputStream;
        mOutputStream = outputStream;
        this.mBaseDataCallback = baseDataCallback;
        setPriority(priority);
        handler = new Handler(Looper.getMainLooper()) ;
        this.isLog = isLog;
    }

    boolean isRunning = false;

    @Override
    public void runTask() {
        boolean isSend = sendData();
        String receivedData = "";
        if (!isSend) {
            return;
        }
        isRunning = true;

        int size;
        // 记录一下发送后的时间，用来判断接收数据是否超时
        long sendTime = System.currentTimeMillis();
        long waitTime = 0;
        while (isRunning) {
            try {

                if (mBufferedInputStream.available() > 0) {
                    // 更新一下接收数据时间
                    waitTime = System.currentTimeMillis();

                    byte[] received = new byte[2048];
                    size = mBufferedInputStream.read(received);
                    receivedData = ByteUtil.bytes2HexStr(received, 0, size);
                    LogPlus.d("received="+ receivedData);
                    mBaseDataCallback.checkData(received, size, new DataPackCallback() {
                        @Override
                        public void setDataPack(DataPack dataPack) {
                            if (dataPack != null) {
                                //命令码
                                if (mCmdPack.getCheckCommand() == null || mCmdPack.getCheckCommand().size() == 0) {
                                    if (mSendResultCallback != null) {
                                        handler.post(new Runnable() {
                                            @Override
                                            public void run() {
                                                mSendResultCallback.onFailed(
                                                        new BaseSerialPortException(ApiExceptionCode.SERIAL_PORT_ERROR, "248的校验命令不能为空,请设置CmdPack中的checkCommand",
                                                                mCmdPack.getDestinationAddress()));
                                            }
                                        });

                                    }

                                    isRunning = isExist();
                                    return;
                                }

                                String command = ByteUtil.bytes2HexStr(mCmdPack.getCheckCommand().get(0));
                                String readCommand = ByteUtil.bytes2HexStr(dataPack.getCommand());
                                Boolean isSuccess = false;
                                for (int i = 0;i < mCmdPack.getCheckCommand().size();i++){
                                    command = ByteUtil.bytes2HexStr(mCmdPack.getCheckCommand().get(i));
                                    if (readCommand.equalsIgnoreCase(command)) {
                                        final DataPack finalDataPack = dataPack;
                                        isSuccess = true;
                                        //LogPlus.e("获取到结果为:" + finalDataPack);
                                        handler.post(new Runnable() {
                                            @Override
                                            public void run() {
                                                LogFileUtil.getInstance().addLog("获取到结果为:" + finalDataPack);
                                                if (mSendResultCallback != null) {
                                                    mSendResultCallback.onSuccess(finalDataPack,dataPack.isCheckout());
                                                    isRunning = isExist();
                                                    return;
                                                }
                                            }
                                        });


                                    }
                                }
                                if (!isSuccess) {
                                    if (mSendResultCallback != null) {
                                        String finalCommand = command;
                                        handler.post(new Runnable() {
                                            @Override
                                            public void run() {
                                                mSendResultCallback.onFailed(new BaseSerialPortException(ApiExceptionCode.SERIAL_PORT_ERROR,
                                                        "命令码不同,获取到结果为:" + dataPack.toString() + "--校验命令码:" + finalCommand, mCmdPack.getDestinationAddress()));
                                            }
                                        });

                                    }
                                }
                                isRunning = false;
                                return;
                            }
                        }
                    });
                } else {
                    // 暂停一点时间，免得一直循环造成CPU占用率过高
                    SystemClock.sleep(5);
                }

                // 检查释放超时
                boolean isTimeOut = isTimeOut(sendTime, waitTime);
                if (isTimeOut) {
                    if (mSendResultCallback != null) {
                        String finalReceivedData = receivedData;
                        long finalWaitTime = waitTime;
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                if (finalWaitTime == 0) {
                                    mSendResultCallback.onFailed(
                                            new BaseSerialPortException(ApiExceptionCode.SERIAL_PORT_READ_OUT_TIME_ERROR_NOT_RECEIVED, "读取超时", mCmdPack.getDestinationAddress()));
                                }else {
                                    mSendResultCallback.onFailed(
                                            new BaseSerialPortException(ApiExceptionCode.SERIAL_PORT_READ_OUT_TIME_ERROR_RECEIVED, "读取超时"+ finalReceivedData, mCmdPack.getDestinationAddress()));

                                }
                            }
                        });

                    }
                    return;
                }
            } catch (IOException e) {
                if (mSendResultCallback != null) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            mSendResultCallback.onFailed(
                                    new BaseSerialPortException(ApiExceptionCode.SERIAL_PORT_ERROR, e.toString(), mCmdPack.getDestinationAddress()));
                        }
                    });

                }
                isRunning = false;
            }
        }
    }

    private boolean isTimeOut(long sendTime, long waitTime) {
        // 表示一直没收到数据
        if (waitTime == 0) {
            long sendOffset = Math.abs(System.currentTimeMillis() - sendTime);
            return sendOffset > mCmdPack.getSendOutTime();
        } else {
            // 有接收到过数据，但是距离上一个数据已经超时
            long waitOffset = Math.abs(System.currentTimeMillis() - waitTime);
            return waitOffset > mCmdPack.getWaitOutTime();
        }
    }

    private boolean sendData() {
        try {
            if (mSendResultCallback != null) {
                mSendResultCallback.onStart(mCmdPack);
            }
            SystemClock.sleep(100);
            SerialPortLoggerFactory.info("发送码:" + ByteUtil.bytes2HexStr(mCmdPack.getSendData()),isLog);
            mOutputStream.write(mCmdPack.getSendData());
        } catch (IOException e) {
            if (mSendResultCallback != null) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        mSendResultCallback.onFailed(
                                new BaseSerialPortException(ApiExceptionCode.SERIAL_PORT_ERROR, "硬件错误:" + e.toString(), mCmdPack.getDestinationAddress()));
                    }
                });
            }
            return false;
        }
        return true;
    }

    /**
     * 串口缓冲区是否还存在数据
     * @return
     */
    private boolean isExist(){
        SystemClock.sleep(50);
        try {
            if (mBufferedInputStream.available() > 0){
                return true;
            }else {
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
}
